/*
 * Copyright (C) 2012-2016 TeraCN
 * 
 * This file is part of TeraCN.
 * 
 * TeraCN is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * TeraCN is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package tera.gameserver.network.serverpackets;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import tera.gameserver.model.equipment.Equipment;
import tera.gameserver.model.equipment.Slot;
import tera.gameserver.model.inventory.Cell;
import tera.gameserver.model.inventory.Inventory;
import tera.gameserver.model.items.BindType;
import tera.gameserver.model.items.CrystalInstance;
import tera.gameserver.model.items.CrystalList;
import tera.gameserver.model.items.ItemInstance;
import tera.gameserver.model.playable.Player;
import tera.gameserver.network.ServerPacketType;

/**
 * @author Ronn Mod Evestu
 */
public class S_Inven extends ServerPacket
{
	private static final ServerPacket instance = new S_Inven();
	
	/**
	 * Method getInstance.
	 * @param player Player
	 * @return S_Inven
	 */
	public static S_Inven getInstance(Player player)
	{
		final S_Inven packet = (S_Inven) instance.newInstance();
		final ByteBuffer buffer = packet.getPrepare();
		
		try
		{
			final Inventory inventory = player.getInventory();
			final Equipment equipment = player.getEquipment();
			
			if ((inventory == null) || (equipment == null))
			{
				return packet;
			}
			
			inventory.lock();
			
			try
			{
				equipment.lock();
				
				try
				{
					final int total = inventory.getEngagedCells() + equipment.getEngagedSlots();
					packet.writeShort(buffer, total);
					packet.writeShort(buffer, 51);
					packet.writeInt(buffer, player.getObjectId());
					packet.writeInt(buffer, player.getSubId());
					packet.writeLong(buffer, inventory.getMoney());
					packet.writeByte(buffer, 0);
					packet.writeByte(buffer, 1);
					final int equipmentSize = equipment.size();
					final int inventorySize = inventory.getMaxCells();
					packet.writeByte(buffer, 0);
					packet.writeInt(buffer, inventorySize);
					packet.writeInt(buffer, 0x1);
					packet.writeInt(buffer, 0x1);
					packet.writeInt(buffer, 0x0);
					packet.writeInt(buffer, 0x0);
					packet.writeInt(buffer, 0x0);
					final Cell[] cells = inventory.getCells();
					final Slot[] slots = equipment.getSlots();
					int n = 0x33;
					
					for (int i = 0; i < equipmentSize; i++)
					{
						final ItemInstance item = slots[i].getItem();
						
						if (item == null)
						{
							continue;
						}
						
						packet.writeShort(buffer, n);
						n += 151;
						packet.writeShort(buffer, n);
						packet.writeInt(buffer, 0);
						packet.writeShort(buffer, n - 2);
						packet.writeInt(buffer, item.getItemId());
						packet.writeInt(buffer, item.getObjectId());
						packet.writeInt(buffer, 0);
						packet.writeInt(buffer, player.getObjectId());
						packet.writeInt(buffer, 0);
						packet.writeInt(buffer, i + 1);
						packet.writeInt(buffer, 0);
						packet.writeLong(buffer, item.getItemCount());
						packet.writeByte(buffer, 0x1E);
						final CrystalList crystals = item.getCrystals();
						
						if ((crystals == null) || crystals.isEmpty())
						{
							packet.writeInt(buffer, 0);
							packet.writeInt(buffer, 0);
							packet.writeInt(buffer, 0);
							packet.writeInt(buffer, 0);
						}
						else
						{
							final int diff = 4 - crystals.size();
							final CrystalInstance[] array = crystals.getArray();
							
							for (int g = 0, length = crystals.size(); g < length; g++)
							{
								packet.writeInt(buffer, array[g].getItemId());
							}
							
							if (diff > 0)
							{
								for (int g = 0; g < diff; g++)
								{
									packet.writeInt(buffer, 0);
								}
							}
						}
						
						packet.writeByte(buffer, 0);
						packet.writeShort(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeShort(buffer, 0);
						packet.writeShort(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeInt(buffer, 0);
						packet.writeShort(buffer, 0);
						packet.writeByte(buffer, 0);
						packet.writeInt(buffer, 0xFFFFFFFF);
						packet.writeInt(buffer, 0);
						packet.writeShort(buffer, 0);
					}
					
					final int last = inventory.getLastIndex();
					
					for (int i = 0; i < inventorySize; i++)
					{
						final ItemInstance item = cells[i].getItem();
						
						if (item == null)
						{
							continue;
						}
						
						packet.writeShort(buffer, n);
						n += 151;
						
						if (last == i)
						{
							n = 0;
						}
						
						packet.writeShort(buffer, n);
						packet.writeInt(buffer, 0);
						
						if (n != 0)
						{
							packet.writeShort(buffer, n - 2);
						}
						else
						{
							packet.writeShort(buffer, 0);
						}
						
						packet.writeInt(buffer, item.getItemId());
						packet.writeInt(buffer, item.getObjectId());
						packet.writeInt(buffer, 0);
						packet.writeInt(buffer, player.getObjectId());
						packet.writeInt(buffer, player.getSubId());
						packet.writeInt(buffer, i + 20);
						packet.writeInt(buffer, 0);
						packet.writeLong(buffer, item.getItemCount());
						packet.writeByte(buffer, (item.getBoundType() == BindType.NONE) || item.isBinded() ? 1 : 0);
						final CrystalList crystals = item.getCrystals();
						
						if ((crystals == null) || crystals.isEmpty())
						{
							packet.writeInt(buffer, 0);
							packet.writeInt(buffer, 0);
							packet.writeInt(buffer, 0);
							packet.writeInt(buffer, 0);
						}
						else
						{
							final int diff = 4 - crystals.size();
							final CrystalInstance[] array = crystals.getArray();
							
							for (int g = 0, length = crystals.size(); g < length; g++)
							{
								packet.writeInt(buffer, array[g].getItemId());
							}
							
							if (diff > 0)
							{
								for (int g = 0; g < diff; g++)
								{
									packet.writeInt(buffer, 0);
								}
							}
						}
						
						packet.writeByte(buffer, 0);
						packet.writeShort(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeShort(buffer, 0);
						packet.writeShort(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeLong(buffer, 0);
						packet.writeInt(buffer, 0);
						packet.writeShort(buffer, 0);
						packet.writeByte(buffer, 0);
						packet.writeInt(buffer, 0xFFFFFFFF);
						packet.writeInt(buffer, 0);
						packet.writeShort(buffer, 0);
					}
				}
				
				finally
				{
					equipment.unlock();
				}
			}
			
			finally
			{
				inventory.unlock();
			}
			return packet;
		}
		
		finally
		{
			buffer.flip();
		}
	}
	
	private final ByteBuffer prepare;
	
	public S_Inven()
	{
		prepare = ByteBuffer.allocate(1024000).order(ByteOrder.LITTLE_ENDIAN);
	}
	
	/**
	 * Method finalyze.
	 * @see rlib.util.pools.Foldable#finalyze()
	 */
	@Override
	public void finalyze()
	{
		prepare.clear();
	}
	
	/**
	 * Method getPacketType.
	 * @return ServerPacketType
	 */
	@Override
	public ServerPacketType getPacketType()
	{
		return ServerPacketType.S_INVEN;
	}
	
	/**
	 * Method isSynchronized.
	 * @return boolean
	 * @see rlib.network.packets.SendablePacket#isSynchronized()
	 */
	@Override
	public boolean isSynchronized()
	{
		return false;
	}
	
	/**
	 * Method writeImpl.
	 * @param buffer ByteBuffer
	 */
	@Override
	protected void writeImpl(ByteBuffer buffer)
	{
		writeOpcode(buffer);
		final ByteBuffer prepare = getPrepare();
		buffer.put(prepare.array(), 0, prepare.limit());
	}
	
	/**
	 * Method getPrepare.
	 * @return ByteBuffer
	 */
	public ByteBuffer getPrepare()
	{
		return prepare;
	}
}